LIFF MockでStorybookにLIFFアプリコンポーネントを作成する
はじめに
アノテーション株式会社 LINE/DevOpsチームの草竹です!
この記事では、LIFFアプリ(LINEミニアプリ)のUIコンポーネントをStorybookで管理してみた流れを紹介します。
外部と通信するコンポーネントをStorybookで管理する際に必要になってくるのがリクエストのモック化です。
MSWを用いることが多いのではないでしょうか。
今回は、LIFF APIをモック化する手段として公式プラグインのLIFF Mockを使ってみました。
この記事で紹介する内容
- LIFF Mockの使い方
- StorybookでLIFF Mockを扱う方法
概要
- LIFF Mockとは
- LIFF Mockのセットアップ
- Storybookとの統合
- 実際の使用例
- まとめ
動作環境
項目 | 値 |
---|---|
@line/liff | 2.24.0 |
@line/liff-mock | 1.0.3 |
storybook | 8.2.7 |
react | 18.3.1 |
vite | 5.3.5 |
typescript | 5.5.4 |
LIFF Mockとは
LINE Developers より引用。
LIFF Mockは、LIFFアプリのテストを簡単にするためのLIFFプラグインです。LIFF Mockを使うと、LIFF SDKにモックモードを追加できます。モックモードでは、LIFFアプリがLIFFサーバーから独立し、LIFF APIがモックデータを返すため、単体テストや負荷テストをより簡単に行うことができます。[1]
LIFF Mockは公式LIFFプラグインの一つ[2]です。LIFFアプリの初期化 liff.init()
時に {mock: true}
を付与するだけでかんたんに始めることができます。
LIFF Mockのセットアップ
インストール手順はLIFF MockのGitHubでも紹介されていますので、あわせてご確認ください。
パッケージをインストールします。
npm i @line/liff @line/liff-mock
line/liff-mock
のサンプルコードを参考に、 LIFF
の型を拡張します。
liff.init()
のオプションに mock
を追加したり、liff.$mock
を使えるようにTypeScriptに認識させるため必要です。
import { ExtendedInit, LiffMockApi } from "@line/liff-mock";
declare module "@line/liff" {
interface Liff {
init: ExtendedInit;
$mock: LiffMockApi;
}
}
LIFFアプリの初期化関数をモジュール化しておきます。
// LIFF Mock
import { liff } from "@line/liff";
import LiffMockPlugin from "@line/liff-mock";
export const liffInit = async () => {
liff.use(new LiffMockPlugin());
await liff
.init({ liffId: import.meta.env.VITE_LIFF_ID, mock: true })
// liff.login() が呼ばれている必要があるため
if (!liff.isInClient()) liff.login()
};
これでLIFF APIのモック化ができるようになりました!
モックデータをセットするには、[メソッド, 戻り値]の[key, value]を liff.$mock.set()
に渡してあげればOKです。
今回の例ではLINEプロフィールを表示するメソッド liff.getProfile()
をモック化してみます。
このように、getProfile
に対して、返してほしい値を渡してあげます。
liff.$mock.set({
getProfile: {
displayName: 'KUSATAKE Daisuke',
userId: '123456789',
pictureUrl,
statusMessage: 'お昼休みはもくもくモッキング',
}
})
Storybookとの統合
StorybookにLIFF Mockを統合するためにやるべきことが2つあります。
liff.init()
でLIFFアプリの初期化liff.$mock.set()
でモックデータのセット
私は次のように行いました。
LIFF API | Storybook関連ファイル | 実行タイミング |
---|---|---|
liff.init() |
.storybook/preview.ts |
- |
liff.$mock.set() |
.src/**/*.stories.ts |
Story.beforeEach |
順を追って説明します。
liff.init()
でLIFFアプリの初期化
1. liff.init()
での初期化は、プロダクションコードと同様に、他のLIFF APIが実行されるより前に行っておく必要があります。
通常、src/main.tsx
等のエントリーポイントで liff.init()
の実行完了を待ってからDOMレンダリングするなどするでしょう。
Storybookでは各ストーリー *.stories.ts
のエントリーポイントは .storybook/preview.ts
です。ここで初期化します。[4]
import type { Preview } from "@storybook/react";
import { liffInit } from "../src/lib/liff-init.mock.ts";
import "src/index.css";
await liffInit()
const preview: Preview = {};
export default preview;
liff.$mock.set()
でモックデータのセット
2. 次に、各ストーリーで liff.$mock.set()
を実行し、本来LIFFサーバーから得る値を任意のモックデータに改竄します。
Story.beforeEach
はStoryが呼び出される前に実行されるため、ここでStoryごとにモックデータをセットします。
// ログインしていて、ユーザープロフィールが取得できているシナリオ
export const LoggedIn: Story = {
beforeEach: () => {
liff.$mock.set({
getProfile: {
userId: "U1234567890",
displayName: "草竹 大輔",
pictureUrl,
statusMessage: "お昼休みはもくもくモッキング",
},
});
},
};
// ログインされておらず、ユーザープロフィールが取得できていないシナリオ
export const LoggedOut: Story = {
beforeEach: () => {
liff.$mock.set({
getProfile: {
isLoggedIn: false,
}
});
},
};
これをliff.getProfile()
を内部で実行しているコンポーネントのストーリーとして適用させます。
サンプルコンポーネントとして UserProfile.tsx を作成しました。
Storybookを立ち上げて確認すると、モックデータをセットできていることがわかります!モックデータ通りのプロフィール情報が表示されていますね。
他、任意のコンポーネント・LIFF APIについても同様の手順でモックデータをセットすれば実現可能なはずです。
まとめ
LIFF MockとStorybookを統合する際、2つのポイントに注意しました。
- LIFF Mockの初期化は
.storybook/preview.ts
で行う - LIFF Mockのモックデータのセットは各ストーリーファイル
src/**/*.stories.ts
で行う
最後までお読みいただきありがとうございました。
今回作成したサンプルプロジェクトを添付します。参考になれば幸いです。
アノテーション株式会社について
アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。
サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。
当社は様々な職種でメンバーを募集しています。
「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイト をぜひご覧ください。
2024/08/05 現在、他にLIFF Inspectorがあります。 ↩︎
LIFF Mockの実装を参照すると、
liff.login()
が呼ばれていることが前提となることがわかります。 ↩︎